﻿using System.Linq.PageList;
using Dyao.Book.Application.Abstractions;
using Dyao.Book.BusApplication;
using Dyao.Book.Contract;
using Dyao.Book.Core;
using Dyao.Book.Entity;
using Dyao.Book.Repository.Abstractions;
using Foundation.Caching;
using Foundation.Core;
using Foundation.DistributeLock;
using Foundation.IdCreate;
using Foundation.ObjectTracing;
using Foundation.Uow;

namespace Dyao.Book.Application
{
    public class BookMgrApplication : BaseApplication, IBookMgrApplication
    {
        public readonly IBookMgrRepository _bookMgrRepository;

        public readonly IBookRepository _bookRepository;

        public readonly ICommonManager _commonManager;

        private readonly IDistributedMultiLevelCache _distributedMultiLevelCache;

        public BookMgrApplication(IBookMgrRepository bookMgrRepository,
            IBookRepository bookRepository,
            ICommonManager commonManager,
            IDistributedMultiLevelCache distributedMultiLevelCache)
        {
            _bookMgrRepository = bookMgrRepository;
            _bookRepository = bookRepository;
            _commonManager = commonManager;
            _distributedMultiLevelCache = distributedMultiLevelCache;
        }


        /// <inheritdoc cref="IBookMgrApplication.GetPageListAsync"/>
        public async Task<IPageList<BookResultDto>> GetPageListAsync(IPageFilter<BookFilterDto> pageFilter, CancellationToken cancellationToken = default)
        {
            var result = new PageList<BookResultDto>();
            using var unitOfWork = UnitOfWorkManager.Begin(isReadDb: true);

            var pageList = await _bookMgrRepository.GetPageListAsync(pageFilter, cancellationToken);

            if (pageList.Records == 0) return result;

            var resultList = ObjectMapper.Map<List<BookEntity>, List<BookResultDto>>(pageList.Data);

            return (PageList<BookResultDto>)pageList.ToMap(resultList);
        }

        /// <inheritdoc cref="IBookMgrApplication.AddAsync"/>
        public async Task<string> AddAsync(BookInfoDto book, CancellationToken cancellationToken = default)
        {
            var idCreate = GetService<IIdCreater>();
            var code = await idCreate.CreateAsync(IdBusinessType.BookRedisId, null, cancellationToken);
            var bookEntity = ObjectMapper.Map<BookInfoDto, BookEntity>(book);
            bookEntity.ID = code.ToString();

            using (var unitOfWork = UnitOfWorkManager.Begin(isTransactional: true))
            {
                try
                {
                    var log = new OperationLogDto()
                    {
                        OperationType = OperationTypeEnum.Book,
                        SourceId = bookEntity.ID,
                        OperationLog = $"添加书籍:{book.Title}"
                    };
                    await _bookRepository.InsertAsync(bookEntity, false, cancellationToken);
                    await _commonManager.AddOperationLogsAsync(log);
                    await unitOfWork.SaveChangesAsync(cancellationToken);
                    await unitOfWork.CompleteAsync(cancellationToken);
                }
                catch (Exception)
                {
                    await unitOfWork.RollbackAsync(cancellationToken);
                    throw;
                }
            }

            await _distributedMultiLevelCache.SetAsync<List<SelectItem>>(CacheConstants.BookSelectItemCache, new List<SelectItem>(), TimeSpan.FromSeconds(1)); // 清除缓存

            return bookEntity.ID;
        }

        /// <inheritdoc cref="IBookMgrApplication.ModifyAsync"/>
        public async Task<string> ModifyAsync(BookInfoDto book, CancellationToken cancellationToken = default)
        {
            if (book.Id.IsNullOrEmpty())
                throw new BusinessException("BookMgr_Modify_Id", "书籍编号不能为空");

            using (var unitOfWork = UnitOfWorkManager.Begin(isTransactional: true))
            {
                var bookEntity = await _bookRepository.GetByIdAsync(book.Id, cancellationToken);
                if (bookEntity == null)
                    throw new BusinessException("BookMgr_Modify_BookIsNull", "书籍信息不存在");
                try
                {
                    var bookProxy = bookEntity.AsTracing(out var bookTracing);

                    bookProxy.Author = book.Author;
                    bookProxy.Description = book.Description;
                    bookProxy.Category = book.Category;
                    bookProxy.SubCategory = book.SubCategory;
                    bookProxy.Title = book.Title;
                    bookProxy.SourceType = book.SourceType;
                    bookProxy.SpiderUrl = book.SpiderUrl;
                    bookProxy.CoverPicture = book.CoverPicture;
                    bookProxy.Completed = book.Completed;

                    if (bookTracing.IsChanged)
                    {
                        var changedLog = bookTracing.TracingReport();
                        var log = new OperationLogDto()
                        {
                            OperationType = OperationTypeEnum.Book,
                            SourceId = bookEntity.ID,
                            OperationLog = $"修改书籍:{changedLog}"
                        };
                        await _commonManager.AddOperationLogsAsync(log);
                    }

                    bookProxy.UpdateUserId = CurrentUser.GetUserId().ToInt();
                    bookProxy.UpdateUserName = CurrentUser.UserName;
                    bookProxy.UpdateDateTime = DateTime.Now;

                    await unitOfWork.SaveChangesAsync(cancellationToken);
                    await unitOfWork.CompleteAsync(cancellationToken);
                }
                catch (Exception)
                {
                    await unitOfWork.RollbackAsync(cancellationToken);
                    throw;
                }
            }

            return "修改成功";
        }

        /// <inheritdoc cref="IBookMgrApplication.DeleteAsync"/>
        public async Task<string> DeleteAsync(string bookId, CancellationToken cancellationToken = default)
        {
            if (bookId.IsNullOrEmpty())
                throw new BusinessException("BookMgr_Delete_Id", "书籍编号不能为空");

            var bookIds = bookId.SplitDefaultToList(); // 书籍编号

            using (var unitOfWork = UnitOfWorkManager.Begin(isTransactional: true))
            {
                var bookEntities = await _bookRepository.GetListByIdsAsync(bookIds, b => new BookEntity { ID = b.ID, IsDel = b.IsDel, Title = b.Title }, cancellationToken);
                if (bookEntities.IsNullOrEmpty())
                    throw new BusinessException("BookMgr_Delete_BookIsNull", "书籍信息不存在");

                bookEntities.ForEach(b => b.IsDel = true);
                var logs = bookEntities.Select(b => new OperationLogDto()
                {
                    OperationType = OperationTypeEnum.Book,
                    SourceId = b.ID,
                    OperationLog = $"删除书籍:{b.Title}"
                });
                try
                {
                    await _bookRepository.BulkUpdateAsync(bookEntities, b => new { b.IsDel }, cancellationToken);
                    await _commonManager.AddOperationLogsAsync(logs.ToArray());
                    await unitOfWork.SaveChangesAsync(cancellationToken);
                    await unitOfWork.CompleteAsync(cancellationToken);
                }
                catch (Exception)
                {
                    await unitOfWork.RollbackAsync(cancellationToken);
                    throw;
                }
            }
            return "删除成功";
        }

        /// <inheritdoc cref="IBookMgrApplication.SyncBookSourceAsync"/>
        public async Task SyncBookSourceAsync(TaskCenterDto taskCenter, CancellationToken cancellationToken = default)
        {
            await ServiceProvider.BeginSingleTaskAsync(async (context) =>
            {
                var book = context.GetQueryFilterModel<CrawBookSourceDto>();
                var service = GetService<IMatchBookSourceSpider>();
                var locker = GetService<IDistributeLocker>();

                string resource = $"CrawBookSourceUrlAsync_{book.BookId}";
                var redLock = await locker.CreateBlockingLockAsync(resource, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10), TimeSpan.FromSeconds(5), cancellationToken);
                await redLock.UsingNonBlockingLockAsync(async () =>
                {
                    await context.AddProgressAsync(10); //更新进度
                    await service.CrawBookSourceUrlAsync(book, cancellationToken);
                    // 爬取章节内容
                    using (var uow = UnitOfWorkManager.Begin(isReadDb: true))
                    {
                        var bookEntity = await _bookRepository.GetByIdAsync(book.BookId, cancellationToken);
                        var cmd = new SyncBookChapterCommand()
                        {
                            Chapter = new CrawCatalogueDto()
                            {
                                BookId = book.BookId,
                                BookSourceType = bookEntity.SourceType,
                                SpiderUrl = bookEntity.SpiderUrl,
                            }
                        };

                        ServiceBus.SendOneWayCommand(cmd);
                    }
                },
                () =>
                {
                    throw new BusinessException($"正在同步{book.BookName}相关书源信息.");
                });
            }, taskCenter, cancellationToken);
        }
    }
}
